# 完全修复版本 - 解决嵌入模型调用问题
# -- coding: utf-8 --
import sys
import io
from dotenv import load_dotenv

# 设置标准输出的编码为UTF-8
if sys.stdout.encoding != "UTF-8":
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")

import numpy as np
import os
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import (
    RecursiveCharacterTextSplitter,
    CharacterTextSplitter,
)
from langchain_community.vectorstores import FAISS

load_dotenv()


def create_sample_documents():
    """创建示例文档用于分割演示"""
    print("\n📁 创建示例文档")
    print("-" * 40)
    os.makedirs("documents", exist_ok=True)

    sample_content = """人工智能技术全面解析

第一章：机器学习基础

1.1 什么是机器学习
机器学习是人工智能的一个核心领域，它使计算机系统能够从数据中学习和改进，而无需进行明确的编程。机器学习算法通过分析大量数据来识别模式并做出预测或决策。

1.2 主要机器学习类型
监督学习：使用标记数据训练模型，用于分类和回归任务。常见的算法包括线性回归、逻辑回归、支持向量机和决策树。

无监督学习：在未标记数据中发现隐藏模式，用于聚类和关联分析。常见算法包括K均值聚类、主成分分析和关联规则学习。

强化学习：智能体通过与环境互动学习最优行为策略，通过试错获得奖励最大化。代表性算法包括Q学习和深度强化学习。

第二章：深度学习进阶

2.1 神经网络基础
神经网络受人脑结构启发，由相互连接的神经元组成。前馈神经网络是最基本的类型，信息单向流动从输入层到输出层。

2.2 卷积神经网络(CNN)
CNN专门用于处理网格状数据，如图像。它通过卷积层、池化层和全连接层自动提取图像特征，在计算机视觉领域取得突破性成果。

2.3 循环神经网络(RNN)
RNN适合处理序列数据，具有记忆功能。LSTM和GRU是RNN的变体，解决了长序列训练中的梯度消失问题，广泛应用于自然语言处理和时间序列预测。

第三章：大语言模型与应用

3.1 Transformer架构
Transformer是当前大语言模型的基础架构，采用自注意力机制并行处理序列数据，大幅提高了训练效率和模型性能。

3.2 预训练与微调
大语言模型通常先在大规模语料上进行预训练，学习通用语言表示，然后在特定任务上进行微调，适应具体应用场景。

3.3 RAG技术详解
检索增强生成(RAG)结合了信息检索和文本生成，通过从外部知识库检索相关信息来增强LLM的回答准确性和事实性，有效减少幻觉问题。"""

    with open("documents/ai_comprehensive_guide.txt", "w", encoding="utf-8") as f:
        f.write(sample_content)

    print("✅ 已创建综合示例文档: documents/ai_comprehensive_guide.txt")
    return "documents/ai_comprehensive_guide.txt"


def document_splitting_demo():
    """文档分割演示"""
    print("\n✂️ 文档分割演示")
    print("-" * 40)

    # 加载文档
    text_path = "documents/ai_comprehensive_guide.txt"
    loader = TextLoader(text_path, encoding="utf-8")
    documents = loader.load()

    original_doc = documents[0]
    print(f"📄 原始文档信息:")
    print(f"   内容长度: {len(original_doc.page_content)} 字符")
    print(f"   预估段落数: {original_doc.page_content.count(chr(10) + chr(10)) + 1}")

    # 演示不同的分割方法
    print("\n1. 按字符分割")
    print("-" * 20)
    char_splitter = CharacterTextSplitter(
        separator="\n\n",
        chunk_size=300,
        chunk_overlap=50,
        length_function=len,
        is_separator_regex=False,
    )

    char_chunks = char_splitter.split_text(original_doc.page_content)
    print(f"   生成块数: {len(char_chunks)}")
    for i, chunk in enumerate(char_chunks[:3]):
        print(f"   块 {i+1}: {len(chunk)} 字符")
        print(f"     预览: {chunk[:80]}...")

    print("\n2. 递归字符分割（推荐）")
    print("-" * 20)
    recursive_splitter = RecursiveCharacterTextSplitter(
        chunk_size=400,
        chunk_overlap=80,
        length_function=len,
        separators=["\n\n", "\n", "。", "！", "？", "，", " ", ""],
    )

    recursive_chunks = recursive_splitter.split_text(original_doc.page_content)
    print(f"   生成块数: {len(recursive_chunks)}")

    # 显示分割效果
    for i, chunk in enumerate(recursive_chunks):
        print(f"\n   📍 块 {i+1}:")
        print(f"      长度: {len(chunk)} 字符")
        print(f"      内容: {chunk[:100]}...")
        print(f"      元数据: 来自文档 '{original_doc.metadata['source']}'")

    return {
        "original": original_doc,
        "char_chunks": char_chunks,
        "recursive_chunks": recursive_chunks,
    }


class FixedEmbeddings:
    """修复的本地嵌入模型 - 完全兼容新版本 LangChain"""

    def __init__(self, dimension=384):
        self.dimension = dimension
        # 关键词向量库
        self.keyword_vectors = {
            "机器学习": self._create_vector(0.8, 0.3, 0.2),
            "深度学习": self._create_vector(0.3, 0.8, 0.4),
            "神经网络": self._create_vector(0.2, 0.4, 0.8),
            "人工智能": self._create_vector(0.7, 0.5, 0.3),
            "监督学习": self._create_vector(0.6, 0.2, 0.1),
            "无监督学习": self._create_vector(0.5, 0.3, 0.2),
            "强化学习": self._create_vector(0.4, 0.4, 0.3),
            "Transformer": self._create_vector(0.1, 0.7, 0.6),
            "RAG": self._create_vector(0.6, 0.5, 0.7),
        }

    def _create_vector(self, x, y, z):
        """创建语义向量"""
        base_vector = np.zeros(self.dimension)
        base_vector[0] = x
        base_vector[1] = y
        base_vector[2] = z
        base_vector[3:] = np.random.normal(0, 0.05, self.dimension - 3)
        return base_vector.tolist()

    def embed_documents(self, texts):
        """为文档列表生成嵌入向量 - 修复版本"""
        print(f"🔧 正在为 {len(texts)} 个文档生成向量...")
        vectors = []

        for text in texts:
            vector = np.zeros(self.dimension)
            total_weight = 0

            # 基于关键词匹配
            for keyword, keyword_vector in self.keyword_vectors.items():
                if keyword in text:
                    weight = 2.0 if text.startswith(keyword) else 1.0
                    vector += np.array(keyword_vector) * weight
                    total_weight += weight

            if total_weight > 0:
                vector = vector / total_weight
            else:
                vector = np.random.normal(0, 0.1, self.dimension)

            vectors.append(vector.tolist())

        return vectors

    def embed_query(self, text):
        """为单个查询生成嵌入向量"""
        return self.embed_documents([text])[0]


def working_embedding_demo():
    """可工作的向量化演示"""
    print("\n🔢 向量化演示")
    print("-" * 40)

    print("🔄 初始化本地嵌入模型...")
    embeddings = FixedEmbeddings(dimension=384)

    # 测试文本
    test_texts = [
        "机器学习是人工智能的核心技术",
        "深度学习使用神经网络处理复杂模式",
        "监督学习需要标记数据进行训练",
        "无监督学习可以发现数据中的隐藏模式",
        "今天天气很好，适合户外运动",
    ]

    print("🧪 生成文本向量...")
    vectors = embeddings.embed_documents(test_texts)

    print(f"✅ 成功生成 {len(vectors)} 个文本向量")
    print(f"📊 向量维度: {len(vectors[0])} 维")

    # 显示向量信息
    print("\n📈 向量分析:")
    for i, (text, vector) in enumerate(zip(test_texts, vectors)):
        print(f"\n   文本 {i+1}: {text}")
        preview = ", ".join([f"{v:.4f}" for v in vector[:5]])
        print(f"   向量预览: [{preview}, ...]")
        print(f"   向量范数: {np.linalg.norm(vector):.4f}")

    # 计算相似度
    print("\n🔍 语义相似度分析:")
    for i in range(len(test_texts)):
        similarities = []
        for j in range(len(test_texts)):
            vec1, vec2 = np.array(vectors[i]), np.array(vectors[j])
            similarity = np.dot(vec1, vec2) / (
                np.linalg.norm(vec1) * np.linalg.norm(vec2)
            )
            similarities.append(f"{similarity:.3f}")

        print(f"   '{test_texts[i][:15]}...' → [{', '.join(similarities)}]")

    return embeddings


def create_working_vector_store(split_docs, embeddings):
    """创建可工作的向量存储"""
    print("\n🗄️ 向量数据库创建")
    print("-" * 40)

    if not embeddings:
        print("❌ 请先完成嵌入模型初始化")
        return None

    try:
        # 使用分割后的文档
        texts = split_docs["recursive_chunks"]
        print(f"📝 使用 {len(texts)} 个文本块创建向量数据库...")

        # 创建FAISS向量存储 - 使用正确的方式
        from langchain_community.vectorstores.utils import DistanceStrategy
        import faiss

        # 手动创建向量和索引
        print("🔧 手动创建向量索引...")
        document_vectors = embeddings.embed_documents(texts)

        # 创建FAISS索引
        dimension = len(document_vectors[0])
        index = faiss.IndexFlatIP(dimension)  # 使用内积相似度

        # 将向量添加到索引
        vectors_array = np.array(document_vectors).astype("float32")
        index.add(vectors_array)

        print(f"✅ 向量索引创建成功!")
        print(f"📊 索引大小: {index.ntotal} 个向量")

        # 创建简单的向量存储模拟
        class SimpleVectorStore:
            def __init__(self, texts, vectors, index):
                self.texts = texts
                self.vectors = vectors
                self.index = index
                self.dimension = dimension

            def similarity_search(self, query, k=3):
                # 为查询生成向量
                query_vector = embeddings.embed_query(query)
                query_array = np.array([query_vector]).astype("float32")

                # 搜索最相似的k个向量
                scores, indices = self.index.search(query_array, k)

                results = []
                for i, (score, idx) in enumerate(zip(scores[0], indices[0])):
                    if idx < len(self.texts):
                        from langchain_core.documents import Document

                        doc = Document(
                            page_content=self.texts[idx],
                            metadata={"score": float(score), "index": int(idx)},
                        )
                        results.append(doc)

                return results

        vectorstore = SimpleVectorStore(texts, document_vectors, index)

        # 测试搜索功能
        print("\n🔍 搜索功能测试")
        print("-" * 20)

        test_queries = ["机器学习", "神经网络", "深度学习", "RAG技术"]

        for query in test_queries:
            print(f"\n   查询: '{query}'")
            results = vectorstore.similarity_search(query, k=2)

            if results:
                print(f"   找到 {len(results)} 个相关结果:")
                for i, doc in enumerate(results):
                    score = doc.metadata.get("score", 0)
                    content_preview = (
                        doc.page_content[:80] + "..."
                        if len(doc.page_content) > 80
                        else doc.page_content
                    )
                    print(f"     {i+1}. 相似度: {score:.3f} | {content_preview}")
            else:
                print(f"   ❌ 未找到相关结果")

        return vectorstore

    except Exception as e:
        print(f"❌ 向量数据库创建失败: {e}")
        import traceback

        traceback.print_exc()
        return None


def practical_demo(vectorstore):
    """实际应用演示"""
    print("\n🚀 实际应用演示")
    print("-" * 40)

    if not vectorstore:
        print("❌ 需要先创建向量数据库")
        return

    # 演示问答场景
    queries = [
        "机器学习有哪些类型？",
        "什么是深度学习？",
        "神经网络有哪些应用？",
        "RAG技术有什么优势？",
    ]

    print("🤖 智能搜索演示:")
    print("-" * 20)

    for query in queries:
        print(f"\n   ❓ 查询: {query}")
        results = vectorstore.similarity_search(query, k=2)

        if results:
            print(f"   📚 找到 {len(results)} 个相关文档:")
            for i, doc in enumerate(results):
                preview = doc.page_content.replace("\n", " ")[:100]
                score = doc.metadata.get("score", 0)
                print(f"      {i+1}. [相似度: {score:.3f}] {preview}...")
        else:
            print(f"   ❌ 未找到相关文档")


def compare_splitting_strategies():
    """比较不同分割策略"""
    print("\n📊 分割策略比较")
    print("-" * 40)

    # 加载文档
    text_path = "documents/ai_comprehensive_guide.txt"
    loader = TextLoader(text_path, encoding="utf-8")
    documents = loader.load()
    original_text = documents[0].page_content

    strategies = [
        ("小尺寸(200)", 200, 40),
        ("中尺寸(400)", 400, 80),
        ("大尺寸(600)", 600, 120),
    ]

    for name, chunk_size, chunk_overlap in strategies:
        splitter = RecursiveCharacterTextSplitter(
            chunk_size=chunk_size, chunk_overlap=chunk_overlap
        )
        chunks = splitter.split_text(original_text)

        print(f"\n🔧 {name}:")
        print(f"   块数: {len(chunks)}")
        avg_length = sum(len(chunk) for chunk in chunks) / len(chunks)
        print(f"   平均长度: {avg_length:.0f} 字符")

        if chunks:
            first_chunk_preview = (
                chunks[0][:60] + "..." if len(chunks[0]) > 60 else chunks[0]
            )
            print(f"   示例: {first_chunk_preview}")


def learning_summary():
    """学习总结"""
    print("\n📚 Day 5 学习总结")
    print("-" * 40)

    summary_points = [
        "✅ 文档分割技术",
        "   - 掌握了字符分割和递归分割两种方法",
        "   - 理解了chunk_size和chunk_overlap参数的作用",
        "   - 学会了根据文档类型选择合适的分割策略",
        "",
        "✅ 文本向量化原理",
        "   - 理解了文本到向量的转换过程",
        "   - 掌握了语义相似度的计算方法",
        "   - 学会了构建本地嵌入模型",
        "",
        "✅ 向量数据库应用",
        "   - 掌握了FAISS向量数据库的基本使用",
        "   - 理解了向量索引的创建和搜索原理",
        "   - 学会了基于语义的文档检索",
        "",
        "✅ 实际问题解决",
        "   - 解决了网络连接问题",
        "   - 修复了嵌入模型调用错误",
        "   - 掌握了离线环境下的解决方案",
        "",
        "🎯 核心收获",
        "   - 文档处理的全流程：加载→分割→向量化→检索",
        "   - RAG系统的基础构建能力",
        "   - 实际问题调试和解决能力",
    ]

    for point in summary_points:
        print(point)


def next_steps_recommendation():
    """下一步学习建议"""
    print("\n🚀 下一步学习建议")
    print("-" * 40)

    recommendations = [
        "🔹 集成真实LLM模型",
        "   - 接入DeepSeek等大语言模型",
        "   - 实现完整的RAG问答系统",
        "   - 学习提示工程和对话管理",
        "",
        "🔹 优化文档处理流程",
        "   - 尝试不同的分割策略",
        "   - 优化向量化参数",
        "   - 处理多种文档格式（PDF、Word等）",
        "",
        "🔹 部署和性能优化",
        "   - 学习生产环境部署",
        "   - 优化搜索性能和准确率",
        "   - 实现缓存和批量处理",
        "",
        "🔹 扩展应用场景",
        "   - 构建企业知识库系统",
        "   - 开发智能客服机器人",
        "   - 创建个性化学习助手",
    ]

    for rec in recommendations:
        print(rec)


if __name__ == "__main__":
    print("🎯 Day 5 - 文档分割与向量化学习 (完全修复版)")
    print("=" * 50)
    print("💡 此版本修复了所有已知问题，确保顺利运行")

    # 创建示例文档
    create_sample_documents()

    # 文档分割演示
    split_results = document_splitting_demo()

    # 向量化演示
    embeddings = working_embedding_demo()

    # 创建向量数据库
    vectorstore = create_working_vector_store(split_results, embeddings)

    # 比较分割策略
    compare_splitting_strategies()

    # 实际应用演示
    if vectorstore:
        practical_demo(vectorstore)

    # 学习总结
    learning_summary()

    # 下一步建议
    next_steps_recommendation()

    print("\n" + "=" * 50)
    print("🎉 Day 5 学习完成！")
    print("\n🌟 成就解锁:")
    print("   📍 掌握了文档分割的核心技术")
    print("   📍 理解了向量化的基本原理")
    print("   📍 构建了完整的文档处理流水线")
    print("   📍 解决了实际开发中的技术问题")

    print("\n💪 准备好迎接下一阶段的挑战了吗？")
    print("   Day 6 我们将集成真正的LLM，构建完整的RAG系统！")
